#ifndef __RING_BUFFER_H
#define __RING_BUFFER_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "UMManage.h"

#ifdef __cplusplus
extern "C"
{
#endif

    typedef struct
    {
        uint32_t data;           // 数据数组指针 读写公用，但是该变量是不可变的, 相对于起始地址的偏移量
        uint32_t buffer_size;    // 记录缓冲区大小，读写公用，但是该变量是不可变的
        uint32_t front;          // 读指针 仅读者使用
        uint32_t rear;           // 写指针 仅写者使用
        uint32_t num_call_and_ret; // 记录call和ret的数量，读写公用，仅写者可写，读者只读
        volatile uint32_t count; // 缓冲区中当前元素数量，读者和写者共用
    } CircularBuffer;

    bool isEnoughSpace(CircularBuffer *buffer, uint32_t size);

    /**
     * @brief 写入数据，会同时将_op和data写入缓冲区
     * @param buffer 缓冲区指针
     * @param _op 必须是 call_op_t/ret_op_t 类型的指针
     * @param data 需要写入的数据指针
     * @return 返回值是实际写入的数据大小，如果返回值为0，表示写入失败。如果成功，
     * 返回值为sizeof(call_op_t) + _op->param_len
     */
    uint32_t writeData(CircularBuffer *buffer,
                       op_t* _op,
                       void const *const data);

    /**
     * @brief 读取数据, 会同时读取_op和data. 如果 data 为空，
     * 则可以作为删除数据的操作。但是不要这样使用，建议直接调用 drop 函数
     * @param buffer [in] 缓冲区指针
     * @param _op [out] 必须是 call_op_t/ret_op_t 类型的指针，保存读取出的header，一定不能为空
     * @param data [out] 保存读取出的数据指针
     * @return 如果读取成果，返回值为true，否则返回false
     */
    bool readData(CircularBuffer *buffer, op_t *_op, void *data);

    /**
     * @brief 不读取数据，而是直接将该数据抛弃
     * @param buffer 缓冲区指针
    */
    void drop(CircularBuffer *buffer);

    /**
     * @brief 初始化环形缓冲区
     * @param buffer [in] 缓冲区指针
     * @param data [in] 数据数组指针相对于起始地址（ _um_addr ）的偏移量
     * @param _um_addr [in] UM的起始地址
     * @param buffer_size [in] 缓冲区大小
    */
    void initCircularBuffer(CircularBuffer *buffer,
                        uint32_t data,
                        uint64_t _um_addr,
                        uint32_t buffer_size);

#ifdef __cplusplus
}
#endif

#endif